package pl.ps.wi.monitoring.station.services;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

import org.jibble.simpleftp.SimpleFTP;

import pl.ps.wi.monitoring.station.LocalConfiuration;
import pl.ps.wi.monitoring.station.enums.DataType;
import pl.ps.wi.monitoring.station.interfaces.DataCollector;
/**
* @brief Obsługa komponentów: kamer, karty, czujników temperatury, rygla, klawiatury. 
*
* @package pl.ps.wi.monitoring.station.services
* 
* Pakiet zawiera następujące pliki: CameraSevice.java , CameraSevice.java ,  CardData.java , CardService , DoorLock , KeyboardService , TemperatureService 
*/
/** 
* @brief Obsługuje kamery oraz ich usługi. 
* Obiekty tej klasy zawierają implementacje po interfejsie klasy DataCollector czyli muszą zdefiniować jej metody.
* @date 2010.03.20
* @version 1.00.00

*  Klasa pozwalająca łączyć się z kamerą, tworzyć.
*  plik konfigurujący dla kamery, pobierać z niej dane, przechowywać dane na
*  serwerze ftp, oraz kończyć połączenie z kamerą. Posiada pola niezbędne do 
*  obsługi, takie jak: 
* - CameraService::camID 
* - CameraService::ftp 
* - CameraService::process 
* - CameraService::run 
* - CameraService::oldName 
* - CameraService::root
* - CameraService::restartHack
* - CameraService::freeToRestart 
*  Metody: CameraService(LocalConfiuration conf, String camId, String path), createConfigFile(String camId, String path), stop(), getData(), getType(),
*  getDevId(), stopService().
 */
public class CameraService implements DataCollector {
	/**
	 * Identyfikator kamery.
	 * Pole będące Stringiem identyfikujące kamerkę. Służy do tworzenia pliku konfiguracyjnego,
	 * którego nazwa jest równa właśnie temu polu.
	 */
	private String camId;
	/**
	 * 	Obiekt klasy SimpleFTP będący naszym serwerwem ftp.
	 *  Obiekt służy do obsługi serwera ftp. Służy do przechowywania plików ze zdjęciami z kamery na serwerze.
	 *  Używany w metodzie getData().
	 */
	private SimpleFTP ftp;
	/**
	 *  Obiekt klasy Process będący naszym serwerwem ftp.
	 *  Obiekt służy do obsługi procesu który zaczyna się w konstruktorze. Ten obiekt służy do
	 *  obsługi wejścia i wyjścia z procesu, czekanie na zakończenie, sprawdzanie statusu i zabijanie procesu.
	 */
	private Process process;
	/**
	 * pole logiczne run.
	 * Pole służy do kontroli uruchamiania procesu kamery.
	 */
	private boolean run = true;
	/**
	 *  String przechowujący nazwę pliku.
	 *  Pole sluży do przechowywania nazwy pliku, który jako ostatni został wysłany na serwer ftp.
	 */
	private String oldName;
	/**
	 *  Pole typu File root.
	 *  Pole służy do obsługi aktualnie przygotowywanego pliku od wysłania na serwer. Dane z kamery są zapisywane do 
	 *  pliku root.
	 */
	private final File root;
	/**
	 *  Pole typu int do zabicia procesu procesu.
	 *  Pole służy jako licznik do zabijania procesu obsługi kamery po 5 nieudanych próbach pobrania danych z kamery.
	 */
	private int restartHack = 0;
	/**
	 *  pole logiczne freeToRestart
	 *  Pole służy do kontroli restartowania procesu kamery poprzez kontrolę pola restartHack.
	 */
	private boolean freeToRestart = false;
	/**
	 * Konstruktor korzystający z pliku konfiguracyjnego, ID kamery i ściezki do pliku konfiguracyjnego.
	 * @param conf
	 * @param camId
	 * @param path
	 */
	public CameraService(LocalConfiuration conf, String camId, String path) {
		this.camId = camId;
		final String confFile = createConfigFile(camId, path);
		Runnable command = new Runnable() {
			@Override
			public void run() {
				try {
					ProcessBuilder pBuilder = new ProcessBuilder("webcam", confFile);
					pBuilder.environment().put("LD_PRELOAD", "/usr/lib/libv4l/v4l1compat.so");
					process = pBuilder.start();
					process.waitFor();
					freeToRestart = false;
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					if (run) {
						System.out.println(CameraService.this.camId + ": Process failed ... restarting !");
						run(); // restart process
					}
				}
			}
		};
		new Thread(command).start();
		
		ftp = new SimpleFTP();
		try {
			ftp.connect(conf.getFtpHost(), 21, conf.getFtpUser(), conf.getFtpPass());
			ftp.bin();
			ftp.cwd(path);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		root = new File("/tmp/" + camId);
	}
	/**
	 * Metoda tworząca plik konfiguracyjny programu webcam. Tworzy je wykorzystując ID kamery oraz ścieżkę do plików
	 * @param camId
	 * @param path
	 * @return empty string
	 */
	private String createConfigFile(String camId, String path) {
		String fileName = "/tmp/webcamrc" + camId;
		File conf = new File(fileName);
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(conf));
			out.write("[grab]\n");
			out.write("device = /dev/video" + camId + "\n");
			out.write("text = webcam nr" + camId + "  %Y-%m-%d %H:%M:%S\n");
			out.write("width = 320\n");
			out.write("height = 240\n");
			out.write("delay = 0\n");
			out.write("quality = 70\n");
			out.write("trigger = 1\n");
			out.write("archive = /tmp/" + camId + "/cam-" + camId + "-%Y%m%d%H%M%S.jpg\n");
//			out.write("[ftp]\n");
//			out.write("host = localhost\n");
//			out.write("user = test\n");
//			out.write("pass = test\n");
//			out.write("dir  = /tmp/" + camId + " \n");
//			out.write("file = webcam" + camId + ".jpg\n");
//			out.write("tmp  = uploading" + camId + ".jpg\n");
//			out.write("local = 1\n");
//			out.write("passive = 0\n");
//			out.write("debug = 0\n");
//			out.write("ssh = 0\n");
			out.flush();
			return fileName;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}
	/**
	 * Metoda zatrzymująca proces. Metoda nie używa żadnych argumentow. Wywołuje metodę destroy klasy Process.
	 */
	public void stop() {
		process.destroy();
	}
	/**
	 * Metoda pobierająca dane z kamery. Pobiera dane z kamery, przygotowuje je do wysłania na serwer ftp i wysyła. Po 5 nieudanych próbach zakańcza proces.
	 * Implementuje interfejs DataCollector::getData()
	 * @return oldName
	 */
	@Override
	public String getData() {
		
		String[] fileList = root.list();
		if (fileList != null && fileList.length > 1) {
			freeToRestart = true;
			restartHack = 0;
			String name0 = fileList[0].substring(fileList[0].indexOf("-"+ camId + "-")+3, fileList[0].indexOf("."));
			String name1 = fileList[1].substring(fileList[1].indexOf("-"+ camId + "-")+3, fileList[1].indexOf("."));
			File toSend;
			if (Long.parseLong(name0) < Long.parseLong(name1)) {
				toSend = new File(root.getAbsoluteFile() + "/" + fileList[0]);
			} else {
				toSend = new File(root.getAbsoluteFile() + "/" + fileList[1]);
			}
			
			System.out.println("Sending: " + toSend.getAbsolutePath());
			try {
				ftp.stor(toSend);
				toSend.delete();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
			}
			
			oldName = toSend.getName();
		} else {
			if (freeToRestart) {
				if (restartHack > 5) {
					restartHack = 0;
					process.destroy();
				}
				restartHack++;
			}
		}
		return oldName;
	}
		
		
		
		
		
		
		
		
		
//		String oldFileName = "/tmp/webcam" + camId + ".jpg";
//		File old = new File(oldFileName);
//		SimpleDateFormat date = new SimpleDateFormat("yyyyMMddHHmmss");
//		String fileName = "webcam-" + camId + "-" + date.format(new Date()) + ".jpg";
//
//		try {
//			Runtime.getRuntime().exec("cp " + oldFileName + " " + fileName).waitFor();
//		} catch (InterruptedException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		} catch (IOException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
//		File newFile = new File(fileName);
//		System.out.println("sending file: " + fileName + " " + old.getName());
//		try {
//			ftp.stor(newFile);
//			newFile.delete();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		System.out.println("sending " + fileName);
//		return fileName;
//	}
	/**
	 * Metoda pobierająca typ kamery. Implementuje interfejs DataCollector::getType().
	 */
	@Override
	public DataType getType() {
		return DataType.CAM;
	}
	/**
	 * Pobiera identyfikator kamery. Implementuje interfejs DataCollector::getDevId().
	 * @return camID
	 */
	@Override
	public int getDevId() {
		return Integer.parseInt(camId);
	}
	/**
	 * Metoda kończąca pracę procesu kamery. Czeka na całkowite zakończenie procesu.
	 */
	public void stopService() {
		run = false;
		process.destroy();
		try { // wait for thread
			process.wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
